open Base

type dir = [`V | `H]
type tag =
  | Rect of int * int
  | Arm of dir

type t =
  { mutable tag : tag
  ; mutable children : t list
  ; mutable path : int list Lazy.t
  ; mutable event_handlers : Event.handler list }

let uninit_path =
  lazy (failwith "path not set")

let create tag =
  { tag
  ; children = []
  ; path = uninit_path
  ; event_handlers = [] }

let rec to_string {tag;children;_} = match tag with
  | Rect(w,h) -> Printf.sprintf "Rect(%d,%d)" w h
  | Arm(d) -> Printf.sprintf "%s[%s]"
                (match d with `H -> "H" | `V -> "V")
                (String.concat ~sep:"; " @@ List.map children ~f:to_string)

let rec equal a b =
  Polymorphic_compare.(a.tag = b.tag)
  && List.equal a.children b.children ~equal

let path t = Lazy.force t.path
let set_tag t tag = t.tag <- tag
let set_children t ts = t.children <- ts
let add_event_handler t eh = t.event_handlers <- eh::t.event_handlers

let rec refresh_path rev_pfx t =
  let () = t.path <- lazy (List.rev rev_pfx) in
  List.iteri t.children
    ~f:(fun i t' -> refresh_path (i::rev_pfx) t')

let rect w h = create (Rect(w, h))
let arm dir ts =
  let t = create (Arm(dir)) in
  set_children t ts ; t
